package org.xxpay.service.impl;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.xxpay.core.common.constant.MchConstant;
import org.xxpay.core.common.constant.RetEnum;
import org.xxpay.core.common.exception.ServiceException;
import org.xxpay.core.common.util.MyLog;
import org.xxpay.core.common.util.MySeq;
import org.xxpay.core.common.util.XXPayUtil;
import org.xxpay.core.entity.*;
import org.xxpay.core.service.*;
import org.xxpay.service.dao.mapper.*;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;

/**
 * @author: dingzhiwei
 * @date: 17/12/4
 * @description:
 */
@Service
public class MchAccountServiceImpl implements IMchAccountService {

    private static final MyLog _log = MyLog.getLog(MchAccountServiceImpl.class);

    @Autowired
    private MchInfoMapper mchInfoMapper;

    @Autowired
    private MchAccountMapper mchAccountMapper;

    @Autowired
    private MchAccountHistoryMapper mchAccountHistoryMapper;

    @Autowired
    private IMchInfoService mchInfoService;

    @Autowired
    private IAgentAgentpayPassageService agentAgentpayPassageService;

    @Autowired
    private IAgentAccountService agentAccountService;

    @Autowired
    private IAgentInfoService agentInfoService;

    @Autowired
    private PayDivisionRecordMapper payDivisionRecordMapper;

    @Autowired
    private IPayDivisionMchService payDivisionMchService;

    @Autowired
    private IAgentpayPassageAccountService agentpayPassageAccountService;

    @Autowired
    private IAgentpayPassageService agentpayPassageService;

    @Autowired
    private TransOrderMapper transOrderMapper;

    @Autowired
    private ISysConfigService sysConfigService;

    /**
     * 查询商户账户列表
     * @param offset
     * @param limit
     * @return
     */
    public List<MchAccount> listAll(int offset, int limit) {
        MchAccountExample example = new MchAccountExample();
        example.setOrderByClause("createTime asc");
        example.setLimit(limit);
        example.setOffset(offset);
        return mchAccountMapper.selectByExample(example);
    }

    /**
     * 更新用户可结算金额
     * @param mchId
     * @param totalAmount
     * @return
     */
    @Transactional(transactionManager = "transactionManager", rollbackFor = Exception.class)
    public int updateSettAmount(long mchId, long totalAmount) {
        MchAccount updateRecord = new MchAccount();
        updateRecord.setMchId(mchId);
        updateRecord.setSettAmount(totalAmount);
        updateBalanceAndSafeKey(updateRecord);
        return 1;
    }

    @Override
    public MchAccount findByMchId(Long mchId) {
        return mchAccountMapper.selectByPrimaryKey(mchId);
    }

    @Override
    public Map countBalance() {
        return mchAccountMapper.countBalance();
    }

    /**
     * 查找可用账户(商户账户存在且状态正常)
     * @param mchId
     * @return
     */
    private MchAccount findAvailableAccount(Long mchId) {
        MchAccount mchAccount = findByMchId(mchId);
        if(mchAccount == null || mchAccount.getStatus() != MchConstant.PUB_YES) {
            // 账户不存在
            throw ServiceException.build(RetEnum.RET_SERVICE_ACCOUNT_NOT_EXIST);
        }
        return mchAccount;
    }

    /**
     * 查找可用商户(商户信息存在且状态正常)
     * @param mchId
     * @return
     */
    private MchInfo findAvailableInfo(Long mchId) {
        MchInfo mchInfo = mchInfoMapper.selectByPrimaryKey(mchId);
        if(mchInfo == null || mchInfo.getStatus() != MchConstant.PUB_YES) {
            // 账户不存在
            throw ServiceException.build(RetEnum.RET_SERVICE_MCH_NOT_EXIST);
        }
        return mchInfo;
    }

    /**
     * 插入订单分账记录表
     * @param payOrder
     */
    private void insertDivisionRecord(PayOrder payOrder){

        //商户分账信息
        PayDivisionRecord mchRecord = new PayDivisionRecord();
        mchRecord.setInfoId(payOrder.getMchId()); //商户ID
        mchRecord.setInfoType(MchConstant.SETT_INFO_TYPE_MCH); //代理商
        mchRecord.setIfTypeCode(payOrder.getChannelType()); //接口类型
        mchRecord.setChannelMchId(payDivisionMchService.getChannelMchId(mchRecord.getInfoType(), mchRecord.getInfoId(), payOrder.getChannelType())); // 上游渠道商户号
        mchRecord.setPayOrderId(payOrder.getPayOrderId());
        mchRecord.setDivisionAmount(payOrder.getMchIncome()); // 分账金额
        mchRecord.setStatus(MchConstant.DIVISION_STATUS_INIT); // 未分账 初始状态
        payDivisionRecordMapper.insertSelective(mchRecord);

        //上级代理商分账信息
        if(payOrder.getAgentId() != null){
            PayDivisionRecord agentRecord = new PayDivisionRecord();
            agentRecord.setInfoId(payOrder.getAgentId()); //代理商ID
            agentRecord.setInfoType(MchConstant.SETT_INFO_TYPE_AGENT); //代理商
            agentRecord.setIfTypeCode(payOrder.getChannelType()); //接口类型
            agentRecord.setChannelMchId(payDivisionMchService.getChannelMchId(agentRecord.getInfoType(), agentRecord.getInfoId(), payOrder.getChannelType())); // 上游渠道商户号
            agentRecord.setPayOrderId(payOrder.getPayOrderId());
            agentRecord.setDivisionAmount(payOrder.getAgentProfit()); // 分账金额
            agentRecord.setStatus(MchConstant.DIVISION_STATUS_INIT); // 未分账 初始状态
            payDivisionRecordMapper.insertSelective(agentRecord);
        }

        //一级代理商分账信息
        if(payOrder.getParentAgentId() != null && payOrder.getParentAgentId() != 0){
            PayDivisionRecord parentAgentRecord = new PayDivisionRecord();
            parentAgentRecord.setInfoId(payOrder.getParentAgentId()); //商户ID
            parentAgentRecord.setInfoType(MchConstant.SETT_INFO_TYPE_AGENT); //代理商
            parentAgentRecord.setIfTypeCode(payOrder.getChannelType()); //接口类型
            parentAgentRecord.setChannelMchId(payDivisionMchService.getChannelMchId(parentAgentRecord.getInfoType(), parentAgentRecord.getInfoId(), payOrder.getChannelType())); // 上游渠道商户号
            parentAgentRecord.setPayOrderId(payOrder.getPayOrderId());
            parentAgentRecord.setDivisionAmount(payOrder.getParentAgentProfit()); // 分账金额
            parentAgentRecord.setStatus(MchConstant.DIVISION_STATUS_INIT); // 未分账 初始状态
            payDivisionRecordMapper.insertSelective(parentAgentRecord);
        }

        //平台分账信息
        PayDivisionRecord platRecord = new PayDivisionRecord();
        platRecord.setInfoId(0L); //商户ID
        platRecord.setInfoType((byte)3); //平台
        platRecord.setIfTypeCode(payOrder.getChannelType()); //接口类型
        platRecord.setChannelMchId(payOrder.getChannelMchId()); // 上游渠道商户号
        platRecord.setPayOrderId(payOrder.getPayOrderId());
        platRecord.setDivisionAmount(payOrder.getPlatProfit()); // 分账金额
        platRecord.setStatus(MchConstant.DIVISION_STATUS_INIT); // 未分账 初始状态
        payDivisionRecordMapper.insertSelective(platRecord);

    }


    @Override
    public void resetTodayAmount() {
        MchAccount updateRecord = new MchAccount();
        updateRecord.setTodayIncome(0L);
        updateRecord.setTodayExpend(0L);
        this.mchAccountMapper.updateByExampleSelective(updateRecord, new MchAccountExample());
    }

//↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓更改商户账户余额（ 采用事务行锁机制 ） ↓↓↓↓↓↓↓↓

    @Override
    @Transactional(transactionManager = "transactionManager", rollbackFor = Exception.class)
    public MchAccount updateBalanceAndSafeKey(MchAccount updateMchAccount) {

        MchAccount dbAccount = mchAccountMapper.selectByPrimaryKey(updateMchAccount.getMchId());
        if(!XXPayUtil.genAccountSafeKey(dbAccount).equals(dbAccount.getSafeKey())){
            _log.warn("[账户安全锁]商户账户安全锁校验失败,mchId={}", updateMchAccount.getMchId());
            throw new ServiceException(RetEnum.RET_SERVICE_ACCOUNT_SAFE_ERROR); //安全机制码校验失败，无法继续操作
        }

        //判断是否更新成功
        if(mchAccountMapper.updateBalanceByMchId(updateMchAccount) <= 0){
            throw new ServiceException(RetEnum.RET_SERVICE_MCH_BALANCE_WRONG); //更新失败，触发事物回滚机制
        }
        dbAccount = mchAccountMapper.selectByPrimaryKey(updateMchAccount.getMchId());
        if(dbAccount.getBalance() < 0 || dbAccount.getUnBalance() < 0 || dbAccount.getSecurityMoney() < 0
        || dbAccount.getSettAmount() < 0 || dbAccount.getFrozenMoney() < 0
        || dbAccount.getAgentpayBalance() < 0 || dbAccount.getUnAgentpayBalance() < 0
        || dbAccount.getAvailableBalance() < 0 || dbAccount.getAvailableAgentpayBalance() < 0 || dbAccount.getAvailableSettAmount() < 0) {
            throw new ServiceException(RetEnum.RET_SERVICE_MCH_BALANCE_WRONG); //数据有误，触发事物回滚机制
        }
        mchAccountMapper.updateSafeKey(updateMchAccount.getMchId(), MchConstant.ACCOUNT_SAFE_SIGN_PWD); //更新safeKey
        return dbAccount;
    }

    @Override
    @Transactional(transactionManager = "transactionManager", rollbackFor = Exception.class)
    public void changeBalanceAndInsertHistory(PayOrder payOrder, boolean isOffRecharge, String remark) {

        //判断是否需要分账( 仅收款类型处理分账，商户代付充值不分账（已确认） )
        if(payOrder.getProductType() == MchConstant.PRODUCT_TYPE_PAY && MchConstant.PUB_YES == payOrder.getIsDivision()) {
            insertDivisionRecord(payOrder); //记录分账明细表，由定时任务调起分账接口。
            return ;
        }

        Long mchId = payOrder.getMchId();
        Long mchIncome = payOrder.getMchIncome();

        MchInfo mchInfo = findAvailableInfo(mchId);  //商户信息
        boolean settIsD0 = mchInfoService.reBuildMchInfoSettConfig(mchInfo).getSettMode() == MchConstant.SETT_TYPE_D0; //商户是否D0结算

        // 判断当前 商户代付资金来源,如果来自收款账户则充值到收款账户，否则充值到代付账户
        byte subAmountFrom = MchConstant.AGENTPAY_OUT_ACCOUNT_AGBALANCE; // 默认从代付账户
        String subAmountFromStr = null;

        //更新载体
        MchAccount updateMchAccount = new MchAccount();
        updateMchAccount.setMchId(mchId);

        byte bizType ; //bizType
        String bizItem ;    //bizItem
        boolean isPayOrder = payOrder.getProductType() == MchConstant.PRODUCT_TYPE_PAY;
        if(isPayOrder){ //商户支付订单
            bizType = MchConstant.BIZ_TYPE_TRANSACT;
            bizItem = MchConstant.BIZ_ITEM_PAY;

            updateMchAccount.setBalance(mchIncome); //商户入账金额
            if(settIsD0) updateMchAccount.setSettAmount(mchIncome); //商户D0： 更新商户的可结算金额
            updateMchAccount.setTotalIncome(mchIncome); //总收益累加和今日收益
            updateMchAccount.setTodayIncome(mchIncome);

        }else{ //商户充值订单（在线/ 线下）

            bizType = MchConstant.BIZ_TYPE_RECHARGE;
            bizItem = isOffRecharge ? MchConstant.BIZ_ITEM_OFF : MchConstant.BIZ_ITEM_ONLINE;

            subAmountFromStr = sysConfigService.getValByCode(MchConstant.AGENTPAY_OUT_ACCOUNT_CONFIG_KEY);
            if(StringUtils.isNotEmpty(subAmountFromStr)) {
                subAmountFrom = Byte.parseByte(subAmountFromStr.trim());
            }
            if(subAmountFrom == MchConstant.AGENTPAY_OUT_ACCOUNT_BALANCE){
                updateMchAccount.setBalance(mchIncome);         // 商户入账金额
                updateMchAccount.setSettAmount(mchIncome);      // 更新商户的可结算金额
            }else {
                updateMchAccount.setAgentpayBalance(mchIncome); // 更新商户代付账户
            }
        }

        MchAccount dbAccount = updateBalanceAndSafeKey(updateMchAccount); //更新并返回最新账户记录

        // 记录商户资金流水
        MchAccountHistory mchAccountHistory = new MchAccountHistory();

        mchAccountHistory.setMchId(mchId);  //mchId
        mchAccountHistory.setAgentId(payOrder.getAgentId());  //agentId
        mchAccountHistory.setParentAgentId(payOrder.getParentAgentId());  //parentAgentId
        mchAccountHistory.setOrderAmount(payOrder.getAmount());   //订单金额
        mchAccountHistory.setAgentProfit(payOrder.getAgentProfit());  //代理商分润
        mchAccountHistory.setParentAgentProfit(payOrder.getParentAgentProfit());   //一级代理商分润
        mchAccountHistory.setPlatProfit(payOrder.getPlatProfit());  //平台利润
        mchAccountHistory.setChannelCost(payOrder.getChannelCost());  //渠道费用
        mchAccountHistory.setAmount(mchIncome);  //商户入账金额
        mchAccountHistory.setFee(payOrder.getAmount() - mchIncome);  //商户费用
        mchAccountHistory.setBizType(bizType);   //bizType
        mchAccountHistory.setBizItem(bizItem);  //bizItem
        mchAccountHistory.setOrderId(payOrder.getPayOrderId());  //订单号
        mchAccountHistory.setChannelOrderNo(payOrder.getChannelOrderNo());   //渠道订单号
        mchAccountHistory.setFundDirection(MchConstant.FUND_DIRECTION_ADD);   //增加 / 减少
        mchAccountHistory.setIsAllowSett(MchConstant.PUB_YES);    //是否允许结算： 影响结算定时任务的执行
        mchAccountHistory.setRemark(remark);

        if(isPayOrder){ //商户支付订单
            mchAccountHistory.setAfterBalance(dbAccount.getBalance()); //商户余额
            mchAccountHistory.setMchSettStatus(settIsD0 ? MchConstant.PUB_YES : MchConstant.PUB_NO); // 商户结算状态设置为商户已结算,保证结算任务不去结算该笔

        }else{  //商户充值订单（在线/ 线下）

            if(subAmountFromStr == null) subAmountFromStr = sysConfigService.getValByCode(MchConstant.AGENTPAY_OUT_ACCOUNT_CONFIG_KEY);
            if(StringUtils.isNotEmpty(subAmountFromStr)) {
                subAmountFrom = Byte.parseByte(subAmountFromStr.trim());
            }
            if(subAmountFrom == MchConstant.AGENTPAY_OUT_ACCOUNT_BALANCE){
                mchAccountHistory.setAfterBalance(dbAccount.getBalance());  //商户账户余额
            }else {
                mchAccountHistory.setAfterBalance(dbAccount.getAgentpayBalance());  // 商户代付余额
            }
            mchAccountHistory.setMchSettStatus(MchConstant.PUB_YES);

        }

        mchAccountHistory.setBalance(mchAccountHistory.getAfterBalance() - mchIncome);  //更新前的账户余额
        mchAccountHistory.setRiskDay(0);  //商户风险预存期
        mchAccountHistory.setAgentSettStatus(MchConstant.PUB_NO);   //代理商结算状态
        mchAccountHistory.setParentAgentSettStatus(MchConstant.PUB_NO);  //上级代理商结算状态
        mchAccountHistory.setAgentRiskDay(agentInfoService.getRiskDay(payOrder.getAgentId()));  // 设置代理商风险预存期  //TODO 上级代理商风险预存期怎么设置？？
        mchAccountHistoryMapper.insertSelective(mchAccountHistory);

    }

    @Override
    @Transactional(transactionManager = "transactionManager", rollbackFor = Exception.class)
    public void changeBalanceAndInsertHistory(MchAgentpayRecord mar, String transOrderId) {

        String agentpayOrderId = mar.getAgentpayOrderId();
        Long remitAmount = mar.getRemitAmount(); //打款金额

        String logPrefix = "["+agentpayOrderId+"]代付成功处理：";

        if(mar.getMchType() == MchConstant.MCH_TYPE_PRIVATE) return ; //私有账户不做处理

        //是否从收款账户出款
        boolean outFromBalance = mar.getSubAmountFrom() == MchConstant.AGENTPAY_OUT_ACCOUNT_BALANCE;
        Long mchId = mar.getMchId();
        Long mchChangeAmount = (0 - mar.getSubAmount()); //商户变更金额
        MchInfo mchInfo = findAvailableInfo(mchId);  //商户信息

        //更新载体
        MchAccount updateMchAccount = new MchAccount();
        updateMchAccount.setMchId(mchId);

        if(outFromBalance){   //收款账户出款
            updateMchAccount.setTodayExpend(Math.abs(mchChangeAmount));
            updateMchAccount.setTotalExpend(Math.abs(mchChangeAmount));
            updateMchAccount.setBalance(mchChangeAmount);
            updateMchAccount.setUnBalance(mchChangeAmount);
            updateMchAccount.setSettAmount(mchChangeAmount);

        }else{  //代付余额出款
            updateMchAccount.setAgentpayBalance(mchChangeAmount);
            updateMchAccount.setUnAgentpayBalance(mchChangeAmount);
        }

        MchAccount dbAccount = updateBalanceAndSafeKey(updateMchAccount); //更新并返回最新账户记录

        Long agentFee = 0L; // 获取代理商费用
        Long parentAgentFee = 0L;

        Long agentProfit = 0L;
        Long parentAgentProfit = 0L;

        if(mchInfo.getAgentId() != null) {

            AgentAgentpayPassage ag = agentAgentpayPassageService.findByAgentIdAndPassageId(mchInfo.getAgentId(), mar.getPassageId());
            agentFee = XXPayUtil.getFee(ag.getFeeType(), ag.getFeeRate(), ag.getFeeEvery(), remitAmount);
            agentProfit = (mar.getFee() - agentFee) < 0 ? 0L : (mar.getFee() - agentFee);
        }

        if(mchInfo.getParentAgentId() != null && mchInfo.getParentAgentId() != 0) {

            AgentAgentpayPassage ag = agentAgentpayPassageService.findByAgentIdAndPassageId(mchInfo.getParentAgentId(), mar.getPassageId());
            parentAgentFee = XXPayUtil.getFee(ag.getFeeType(), ag.getFeeRate(), ag.getFeeEvery(), remitAmount);
            parentAgentProfit = (agentFee - parentAgentFee) < 0 ? 0L : (agentFee - parentAgentFee);
        }

        AgentpayPassage passage ;
        AgentpayPassageAccount apa;
        if(mar.getConfirmPassageAccountId() != null && mar.getConfirmPassageAccountId() > 0){
            passage = agentpayPassageService.findById(mar.getConfirmPassageId());
            apa = agentpayPassageAccountService.findById(mar.getConfirmPassageAccountId());
        }else{
            passage = agentpayPassageService.findById(mar.getPassageId());
            apa = agentpayPassageAccountService.findById(mar.getPassageAccountId());
        }

        Long platSupplierProfit = 0L;

        //代付通道的中间值
        Long agentpayPassageCostFee = XXPayUtil.getFee(passage.getFeeType(), passage.getFeeRate(), passage.getFeeEvery(), remitAmount);

        //平台商户侧利润
        Long platMchProfit = 0L;

        //平台利润(商户侧)  : （一级代理商费用 - 通道费用）  或者  （二级代理商费用 - 通道费用）  或者  （商家费用 - 通道费用）
        if (mchInfo.getParentAgentId() != null && mchInfo.getParentAgentId() != 0){
            platMchProfit = parentAgentFee - agentpayPassageCostFee;
        }else if(mchInfo.getAgentId() != null && mchInfo.getAgentId() != 0) {

            platMchProfit = agentFee - agentpayPassageCostFee;
        }else{
            platMchProfit = mar.getFee() - agentpayPassageCostFee;
        }
        if(platMchProfit < 0){
            _log.error("{}平台商户侧利润计算有误！platMchProfit={}", logPrefix, platMchProfit);
            platMchProfit = 0L;
        }


        Long platAllProfit =  (platMchProfit + platSupplierProfit);

        _log.info("{}计算完成， 商户侧利润={}; 总利润={}", logPrefix, platMchProfit, platAllProfit);

        // 记录商户资金流水
        MchAccountHistory mchAccountHistory = new MchAccountHistory();
        mchAccountHistory.setMchId(mchId);  //mchId
        mchAccountHistory.setAgentId(mchInfo.getAgentId());  //agentId
        mchAccountHistory.setParentAgentId(mchInfo.getParentAgentId());  //parentAgentId

        mchAccountHistory.setOrderId(mar.getAgentpayOrderId());  //订单号
        mchAccountHistory.setOrderAmount(mar.getAmount());   //订单金额

        mchAccountHistory.setAgentProfit(agentProfit);  // 代理商利润
        mchAccountHistory.setParentAgentProfit(parentAgentProfit);  // 一级代理利润
        mchAccountHistory.setPlatProfit(platAllProfit); // 平台利润
        mchAccountHistory.setChannelCost(agentpayPassageCostFee); //渠道成本（通道中间值）
        mchAccountHistory.setAmount(Math.abs(mchChangeAmount));  //商户变更金额
        mchAccountHistory.setFee(mar.getFee());  //商户费用
        mchAccountHistory.setBizType(MchConstant.BIZ_TYPE_AGENTPAY);   //bizType
        mchAccountHistory.setBizItem(MchConstant.BIZ_ITEM_AGENTPAY);
        mchAccountHistory.setFundDirection(MchConstant.FUND_DIRECTION_SUB);   //增加 / 减少

        mchAccountHistory.setAfterBalance(outFromBalance ? dbAccount.getBalance() : dbAccount.getAgentpayBalance() );  //商户账户余额 / 代付余额
        mchAccountHistory.setBalance(mchAccountHistory.getAfterBalance() - mchChangeAmount);  //更新前的账户余额

        mchAccountHistory.setIsAllowSett(MchConstant.PUB_YES);    //是否允许结算： 影响结算定时任务的执行
        mchAccountHistory.setMchSettStatus(MchConstant.PUB_YES);
        mchAccountHistory.setAgentSettStatus(MchConstant.PUB_NO);   //代理商结算状态
        mchAccountHistory.setParentAgentSettStatus(MchConstant.PUB_NO);  //上级代理商结算状态
        mchAccountHistory.setRiskDay(0);  //商户风险预存期
        mchAccountHistory.setAgentRiskDay(agentInfoService.getRiskDay(mchInfo.getAgentId()));  // 设置代理商风险预存期

        //设置渠道成本费用
        if(StringUtils.isNotEmpty(transOrderId)){
            TransOrder transOrder = transOrderMapper.selectByPrimaryKey(transOrderId);
            if(transOrder != null ){
                mchAccountHistory.setChannelCost(transOrder.getChannelCost());
            }
        }

        mchAccountHistoryMapper.insertSelective(mchAccountHistory);

    }

    @Override
    @Transactional(transactionManager = "transactionManager", rollbackFor = Exception.class)
    public void changeBalanceAndInsertHistory(SettRecord settRecord) {

        Long mchId = settRecord.getInfoId();
        Long settAmount = settRecord.getSettAmount();
        Long settFee = settRecord.getSettFee();
        Long mchChangeAmount = (0 - settAmount - settFee); //商户变更金额

        MchInfo mchInfo = findAvailableInfo(mchId);  //商户信息

        //更新载体
        MchAccount updateMchAccount = new MchAccount();
        updateMchAccount.setMchId(mchId);
        updateMchAccount.setTodayExpend(Math.abs(mchChangeAmount));
        updateMchAccount.setTotalExpend(Math.abs(mchChangeAmount));
        updateMchAccount.setBalance(mchChangeAmount);
        updateMchAccount.setUnBalance(mchChangeAmount);
        updateMchAccount.setSettAmount(mchChangeAmount);

        MchAccount dbAccount = updateBalanceAndSafeKey(updateMchAccount); //更新并返回最新账户记录

        // 记录商户资金流水
        MchAccountHistory mchAccountHistory = new MchAccountHistory();

        mchAccountHistory.setMchId(mchId);  //mchId
        mchAccountHistory.setAgentId(mchInfo.getAgentId());  //agentId
        mchAccountHistory.setParentAgentId(mchInfo.getParentAgentId());  //parentAgentId
        mchAccountHistory.setOrderAmount(settAmount);   //订单金额
        mchAccountHistory.setAmount(Math.abs(mchChangeAmount));  //商户变更金额
        mchAccountHistory.setFee(settFee);  //商户费用
        mchAccountHistory.setBizType(MchConstant.BIZ_TYPE_REMIT);   //bizType
        mchAccountHistory.setOrderId(settRecord.getSettOrderId());  //订单号
        mchAccountHistory.setFundDirection(MchConstant.FUND_DIRECTION_SUB);   //增加 / 减少

        mchAccountHistory.setAfterBalance(dbAccount.getBalance());  //商户代付余额
        mchAccountHistory.setBalance(mchAccountHistory.getAfterBalance() - mchChangeAmount);  //更新前的账户余额

        mchAccountHistory.setIsAllowSett(MchConstant.PUB_NO);    //是否允许结算： 影响结算定时任务的执行
        mchAccountHistory.setMchSettStatus(MchConstant.PUB_NO);
        mchAccountHistory.setAgentSettStatus(MchConstant.PUB_NO);   //代理商结算状态
        mchAccountHistory.setParentAgentSettStatus(MchConstant.PUB_NO);  //上级代理商结算状态
        mchAccountHistory.setRiskDay(0);  //商户风险预存期
        mchAccountHistoryMapper.insertSelective(mchAccountHistory);

    }

    @Override
    @Transactional(transactionManager = "transactionManager", rollbackFor = Exception.class)
    public void changeBalanceAndInsertHistoryByPlat(Long mchId, Long changeAmount, String bizItem, String remark) {

        if(changeAmount == 0) return ; //更改金额为0, 不新增资金流水表, 无意义

        MchInfo mchInfo = findAvailableInfo(mchId);  //商户信息

        //1. update
        MchAccount updateMchAccount = new MchAccount();
        updateMchAccount.setMchId(mchId);

        switch (bizItem) {
            case MchConstant.BIZ_ITEM_BALANCE:  //账户余额
                updateMchAccount.setBalance(changeAmount);
                updateMchAccount.setSettAmount(changeAmount);
                break;
            case MchConstant.BIZ_ITEM_AGENTPAY_BALANCE: //代付余额
                updateMchAccount.setAgentpayBalance(changeAmount);
                break;
            case MchConstant.BIZ_ITEM_FROZEN_MONEY: //冻结金额
                updateMchAccount.setFrozenMoney(changeAmount);
                break;
            case MchConstant.BIZ_ITEM_SECURITY_MONEY:   //保证金额
                updateMchAccount.setSecurityMoney(changeAmount);
                break;
            case MchConstant.BIZ_ITEM_AGENTPAY_FROZEN_MONEY:   //代付账户冻结金额
                updateMchAccount.setAgentpayFrozenMoney(changeAmount);
                break;
            default:
                throw new ServiceException(RetEnum.RET_SERVICE_ACCOUNT_TYPE_NOT_EXIST);
        }

        MchAccount dbAccount = updateBalanceAndSafeKey(updateMchAccount); //更新并返回最新账户记录

        //2. 记录资金流水
        MchAccountHistory mchAccountHistory = new MchAccountHistory();
        mchAccountHistory.setMchId(mchId);
        mchAccountHistory.setAgentId(mchInfo.getAgentId()); //商户的代理商
        mchAccountHistory.setParentAgentId(mchInfo.getParentAgentId()); //一级代理商
        mchAccountHistory.setAmount(Math.abs(changeAmount)); //取绝对值
        mchAccountHistory.setFundDirection(changeAmount > 0 ? MchConstant.FUND_DIRECTION_ADD : MchConstant.FUND_DIRECTION_SUB);
        mchAccountHistory.setBizType(MchConstant.BIZ_TYPE_CHANGE_BALANCE);
        mchAccountHistory.setBizItem(bizItem);
        mchAccountHistory.setOrderId(MySeq.getChangeAccount());
        mchAccountHistory.setRemark(remark);

        //结算相关
        mchAccountHistory.setIsAllowSett(MchConstant.PUB_NO);  //是否允许结算
        mchAccountHistory.setMchSettStatus(MchConstant.PUB_NO);  //商户结算状态
        mchAccountHistory.setAgentSettStatus(MchConstant.PUB_NO); //代理商结算状态
        mchAccountHistory.setParentAgentSettStatus(MchConstant.PUB_NO); //上级代理商结算状态
        mchAccountHistory.setRiskDay(0);   //代理商风险预存期

        switch (bizItem) {
            case MchConstant.BIZ_ITEM_BALANCE:  //账户余额
                mchAccountHistory.setAfterBalance(dbAccount.getBalance());
                break;
            case MchConstant.BIZ_ITEM_AGENTPAY_BALANCE: //代付余额
                mchAccountHistory.setAfterBalance(dbAccount.getAgentpayBalance());
                break;
            case MchConstant.BIZ_ITEM_FROZEN_MONEY: //冻结金额
                mchAccountHistory.setAfterBalance(dbAccount.getFrozenMoney());
                break;
            case MchConstant.BIZ_ITEM_SECURITY_MONEY:   //保证金额
                mchAccountHistory.setAfterBalance(dbAccount.getSecurityMoney());
                break;
            case MchConstant.BIZ_ITEM_AGENTPAY_FROZEN_MONEY:   //代付账户冻结金额
                mchAccountHistory.setAfterBalance(dbAccount.getAgentpayFrozenMoney());
                break;
        }

        mchAccountHistory.setBalance(mchAccountHistory.getAfterBalance() - changeAmount);
        mchAccountHistoryMapper.insertSelective(mchAccountHistory);

    }

    @Override
    @Transactional(transactionManager = "transactionManager", rollbackFor = Exception.class)
    public void onlyChangeUnBalance(Long mchId, String bizItem, Long changeUnBalance) {

        //更新载体
        MchAccount updateMchAccount = new MchAccount();
        updateMchAccount.setMchId(mchId);
        if(MchConstant.BIZ_ITEM_BALANCE.equals(bizItem)){
            updateMchAccount.setUnBalance(changeUnBalance);
        }else if(MchConstant.BIZ_ITEM_AGENTPAY_BALANCE.equals(bizItem)){
            updateMchAccount.setUnAgentpayBalance(changeUnBalance);
        }

        updateBalanceAndSafeKey(updateMchAccount);
    }

    @Override
    public void recharge4Agentpay2(Long mchId, long inAmount, String remark, String orderId) {

        MchInfo mchInfo = findAvailableInfo(mchId);

        //_log.info("{}trandeNo={}开始为商户充值.rechargeMchId={},inAmount={}元", logP, tradeNo, rechargeMchId, inAmount);

        // 得到该笔充值商户费用
        Long mchFee = XXPayUtil.getFee((byte) 1, mchInfo.getOffRechargeRate(), null, inAmount);
        long chargeAmount = inAmount - mchFee < 0 ? 0 : inAmount - mchFee;

        // 计算代理商分润
        Long agentProfit = 0l;
        Long parentAgentProfit = 0l;
        Long agentId = mchInfo.getAgentId();
        Long parentAgentId = mchInfo.getParentAgentId();
        // 平台利润
        Long platProfit = mchFee;
        if(agentId != null) {
            AgentInfo agentInfo = findAvailableAgentInfo(agentId);
            if(agentInfo != null && agentInfo.getStatus().equals(MchConstant.PUB_YES)) {
                // 计算二级代理商分润
                BigDecimal inFeeRate = agentInfo.getOffRechargeRate();
                Long agentFee = XXPayUtil.getFee((byte) 1, inFeeRate, null, inAmount);
                agentProfit = (mchFee - agentFee) < 0 ? 0 : (mchFee - agentFee);
                // 平台利润
                platProfit = agentFee;
                if(parentAgentId != null && parentAgentId != 0) {
                    AgentInfo parentAgentInfo = findAvailableAgentInfo(parentAgentId);
                    if(parentAgentInfo != null && parentAgentInfo.getStatus().equals(MchConstant.PUB_YES)) {
                        // 计算一级代理商分润
                        BigDecimal parentInFeeRate = parentAgentInfo.getOffRechargeRate();
                        Long parentAgentFee = XXPayUtil.getFee((byte) 1, parentInFeeRate, null, inAmount);
                        parentAgentProfit = (agentFee - parentAgentFee) < 0 ? 0 : (agentFee - parentAgentFee);
                        // 平台利润
                        platProfit = parentAgentFee;
                    }
                }
            }
        }
        // 渠道成本暂定为0
        Long channelCost = 0l;
        platProfit -= channelCost;

        // 填充订单必要信息，调用充值函数
        PayOrder payOrderRecord = new PayOrder();
        payOrderRecord.setMchId(mchInfo.getMchId()); //商户ID, 用于获取商户信息
        payOrderRecord.setMchIncome(chargeAmount); //商户入账金额
        payOrderRecord.setProductType(MchConstant.PRODUCT_TYPE_RECHARGE); //支付产品类型 固定收款类型
        payOrderRecord.setAgentId(mchInfo.getAgentId()); //商户代理商ID
        payOrderRecord.setParentAgentId(mchInfo.getParentAgentId()); //商户总代理ID
        payOrderRecord.setAmount(inAmount); //订单金额
        payOrderRecord.setAgentProfit(agentProfit); //代理商利润
        payOrderRecord.setParentAgentProfit(parentAgentProfit); //总代理利润
        payOrderRecord.setPlatProfit(platProfit); //平台利润
        payOrderRecord.setChannelCost(channelCost); //渠道成本
        payOrderRecord.setPayOrderId(orderId); //支付订单号
        payOrderRecord.setSubject(remark);
        changeBalanceAndInsertHistory(payOrderRecord, true, remark);


    }

//↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑


    /**
     * 查找可用代理商(代理信息存在且状态正常)
     * @param agentId
     * @return
     */
    private AgentInfo findAvailableAgentInfo(Long agentId) {
        AgentInfo agentInfo = agentInfoService.findByAgentId(agentId);
        if(agentInfo == null || agentInfo.getStatus() != MchConstant.PUB_YES) {
            // 账户不存在
            throw ServiceException.build(RetEnum.RET_SERVICE_AGENT_NOT_EXIST);
        }
        return agentInfo;
    }

}
